#include "preHeader.h"
#include "Attribute.h"
#include "Unit.h"

Attribute::Attribute(Unit* pOwner)
: m_pOwner(pOwner)
, m_isAttrDirty(false)
, m_attrPartProxies{
	{pOwner, ATTRPARTTYPE::BASE}, {pOwner, ATTRPARTTYPE::BODY},
	{pOwner, ATTRPARTTYPE::EQUIP}, {pOwner, ATTRPARTTYPE::BUFF}}
{
	std::fill_n(&m_attr[0][0][0], COUNT_OF_3(m_attr), .0);
	std::fill_n(&m_attrEx[0], COUNT_OF_1(m_attrEx), .0);
	std::fill_n(&m_attrCacheValue[0], COUNT_OF_1(m_attrCacheValue), .0);
}

Attribute::~Attribute()
{
}

void Attribute::Reload(uint32 flags)
{
	typedef void (Attribute::*ReloadFunc)();
	static const ReloadFunc funcs[] = {
		&Attribute::ReloadBase,
		&Attribute::ReloadBody,
		&Attribute::ReloadEquip,
		nullptr,
	};
	STATIC_ASSERT(ARRAY_SIZE(funcs)==(int)ATTRPARTTYPE::COUNT);
	for (int part = 0; part < (int)ATTRPARTTYPE::COUNT; ++part) {
		if (funcs[part] != nullptr) {
			(this->*funcs[part])();
		}
	}
}

void Attribute::ReloadPart(ATTRPARTTYPE type)
{
	m_isAttrDirty = true;
	for (int arith = 0; arith < (int)ATTRARITHTYPE::COUNT; ++arith) {
		std::fill_n(&m_attr[arith][(int)type][0], (int)ATTRTYPE::COUNT, .0);
	}
}

void Attribute::ReloadBase()
{
	ReloadPart(ATTRPARTTYPE::BASE);
	m_attrPartProxies[(int)ATTRPARTTYPE::BASE].Reset();
	m_pOwner->LoadBase(m_attr[(int)ATTRARITHTYPE::BASE][(int)ATTRPARTTYPE::BASE]);
}

void Attribute::ReloadBody()
{
	ReloadPart(ATTRPARTTYPE::BODY);
	m_attrPartProxies[(int)ATTRPARTTYPE::BODY].Reset();
	m_pOwner->LoadBody(m_attrPartProxies[(int)ATTRPARTTYPE::BODY]);
}

void Attribute::ReloadEquip()
{
	ReloadPart(ATTRPARTTYPE::EQUIP);
	m_attrPartProxies[(int)ATTRPARTTYPE::EQUIP].Reset();
	m_pOwner->LoadEquip(m_attrPartProxies[(int)ATTRPARTTYPE::EQUIP]);
}

void Attribute::FlushAttrValue()
{
	if (m_isAttrDirty) {
		m_isAttrDirty = false;
		for (int type = (int)ATTRTYPE::NONE + 1; type < (int)ATTRTYPE::COUNT; ++type) {
			m_attrCacheValue[type] = CalcAttrValue((ATTRTYPE)type);
		}
	}
}

void Attribute::ModAttr(ATTRARITHTYPE arith, ATTRPARTTYPE part, ATTRTYPE type, double value)
{
	if (arith >= (ATTRARITHTYPE)0 && arith < ATTRARITHTYPE::COUNT &&
		part >= (ATTRPARTTYPE)0 && part < ATTRPARTTYPE::COUNT &&
		type > ATTRTYPE::NONE && type < ATTRTYPE::COUNT)
	{
		m_attr[(int)arith][(int)part][(int)type] += value;
		m_isAttrDirty = true;
	}
}

void Attribute::ModXAttr(ATTRPARTTYPE part, uint32 type, double value)
{
	ModAttr(ATTRARITHTYPE(type / 100), part, ATTRTYPE(type % 100), value);
}

double Attribute::GetAttr(ATTRTYPE type) const
{
	if (type > ATTRTYPE::NONE && type < ATTRTYPE::COUNT) {
		if (!m_isAttrDirty) {
			return m_attrCacheValue[(int)type];
		} else {
			return CalcAttrValue(type);
		}
	} else {
		return .0;
	}
}

void Attribute::SetAttrEx(ATTREXTYPE type, double value)
{
	if (type > ATTREXTYPE::NONE && type < ATTREXTYPE::COUNT) {
		m_attrEx[(int)type] = value;
	}
}

void Attribute::ModAttrEx(ATTREXTYPE type, double value)
{
	if (type > ATTREXTYPE::NONE && type < ATTREXTYPE::COUNT) {
		m_attrEx[(int)type] += value;
	}
}

double Attribute::GetAttrEx(ATTREXTYPE type) const
{
	if (type > ATTREXTYPE::NONE && type < ATTREXTYPE::COUNT) {
		return m_attrEx[(int)type];
	} else {
		return .0;
	}
}

void Attribute::AttachAttrVex(ATTRVEXTYPE type, double value)
{
	if (type > ATTRVEXTYPE::NONE && type < ATTRVEXTYPE::COUNT) {
		m_attrVex[(int)type].push_back(value);
	}
}

void Attribute::DetachAttrVex(ATTRVEXTYPE type, double value)
{
	if (type > ATTRVEXTYPE::NONE && type < ATTRVEXTYPE::COUNT) {
		auto& attrVexList = m_attrVex[(int)type];
		if (!attrVexList.empty()) {
			auto itr = std::find(
				attrVexList.begin(), attrVexList.end(), value);
			if (itr != attrVexList.end()) {
				attrVexList.erase(itr);
			}
		}
	}
}

const std::vector<double>& Attribute::GetAttrVex(ATTRVEXTYPE type) const
{
	if (type > ATTRVEXTYPE::NONE && type < ATTRVEXTYPE::COUNT) {
		return m_attrVex[(int)type];
	} else {
		return m_attrVex[(int)ATTRVEXTYPE::NONE];
	}
}

void Attribute::AttachAttrSex(ATTRSEXTYPE type, LuaRef&& value)
{
	if (type > ATTRSEXTYPE::NONE && type < ATTRSEXTYPE::COUNT) {
		m_attrSex[(int)type].push_back(std::move(value));
	}
}

void Attribute::DetachAttrSex(ATTRSEXTYPE type, LuaRef&& value)
{
	if (type > ATTRSEXTYPE::NONE && type < ATTRSEXTYPE::COUNT) {
		auto& attrSexList = m_attrSex[(int)type];
		if (!attrSexList.empty()) {
			auto tgtValue = value.Get<LuaFunc>();
			auto itr = std::find_if(
				attrSexList.begin(), attrSexList.end(),
				[&tgtValue](const LuaRef& value) {
				auto curValue = value.Get<LuaFunc>();
				return lua_rawequal(curValue.getL(),
					curValue.index(), tgtValue.index()) != 0;
			});
			if (itr != attrSexList.end()) {
				attrSexList.erase(itr);
			}
		}
	}
}

const std::vector<LuaRef>& Attribute::GetAttrSex(ATTRSEXTYPE type) const
{
	if (type > ATTRSEXTYPE::NONE && type < ATTRSEXTYPE::COUNT) {
		return m_attrSex[(int)type];
	} else {
		return m_attrSex[(int)ATTRSEXTYPE::NONE];
	}
}

double Attribute::GetHitChance(Unit* pTarget) const
{
	const auto& tgtAttr = pTarget->GetAttribute();
	return GetAttr(ATTRTYPE::HIT_CHANCE) - tgtAttr.GetAttr(ATTRTYPE::DODGE_CHANCE);
}

double Attribute::GetCritiHitChance(Unit* pTarget) const
{
	const auto& tgtAttr = pTarget->GetAttribute();
	return GetAttr(ATTRTYPE::CRITIHIT_CHANCE) - tgtAttr.GetAttr(ATTRTYPE::CRITIHIT_CHANCE_RESIST);
}

double Attribute::GetCritiHitIntensity(Unit* pTarget) const
{
	const auto& tgtAttr = pTarget->GetAttribute();
	return GetAttr(ATTRTYPE::CRITIHIT_INTENSITY) - tgtAttr.GetAttr(ATTRTYPE::CRITIHIT_INTENSITY_RESIST);
}

double Attribute::CalcAttackDamage(Unit* pVictim, double attackRate, double attackValue)
{
	const auto& tgtAttr = pVictim->GetAttribute();
	return std::max(GetAttr(ATTRTYPE::ATTACK_VALUE) / tgtAttr.GetAttr(ATTRTYPE::DEFENSE_VALUE) *
		GetAttrEx(ATTREXTYPE::DAMAGE_FACTOR) * attackRate + attackValue, 1.0);
}

double Attribute::SumAttrValue(ATTRARITHTYPE arith, ATTRTYPE type, double initValue) const
{
	double sumValue = initValue;
	for (int part = 0; part < (int)ATTRPARTTYPE::COUNT; ++part) {
		sumValue += m_attr[(int)arith][part][(int)type];
	}
	return std::max(sumValue, .0);
}

double Attribute::CalcAttrValue(ATTRTYPE type) const
{
	double baseValue = SumAttrValue(ATTRARITHTYPE::BASE, type, 0.0);
	double scaleValue = SumAttrValue(ATTRARITHTYPE::SCALE, type, 1.0);
	return SumAttrValue(ATTRARITHTYPE::FINAL, type, baseValue * scaleValue);
}
